import json
import logging

from bbp.ssdev import SsdevClient

TYPE = 'type'
CODE = 'code'
NAME = 'name'
CHILDREN = 'children'
PARENT = 'parent'
ID = 'id'

NODE_PROPERTIES = (TYPE, CODE, NAME, CHILDREN, PARENT, ID)

SOFT = 'soft'
CATEGORY = 'category'
MODULE = 'module'


class ModuleService(object):
    service_id = 'bbp.module'

    def __init__(self, client: SsdevClient):
        self.client = client

    def find_soft(self):
        return self.client.invoke(ModuleService.service_id, 'findSoft')

    def exist_soft(self, soft_code):
        soft_list = self.client.invoke(ModuleService.service_id, 'findSoft')
        soft_id = None
        find_soft = list(filter(lambda s: s['cd'] == soft_code, soft_list))
        if len(find_soft) > 0:
            soft_id = find_soft[0]['id']
        return len(find_soft) > 0, soft_id

    def get_soft(self, soft_id):
        return self.client.invoke(ModuleService.service_id, 'getSoft', soft_id)

    def save_soft(self, soft_code, soft_name):
        req_obj = {"cd": soft_code, "na": soft_name, "softType": "1", "belongOrgId": "", "icon": "", "url": "",
                   "loader": None, "des": "", "py": "", "wb": "", "zj": ""}

        return self.client.invoke(ModuleService.service_id, 'saveSoft', req_obj, 'create')

    # saveModuleCategory
    def save_module_category(self, ca_code, ca_name, soft_id):
        req_obj = {"na": ca_name, "cd": ca_code, "parent": "", "icon": "", "des": "", "py": "", "wb": "", "zj": "",
                   "softId": soft_id}
        return self.client.invoke(ModuleService.service_id, 'saveModuleCategory', req_obj, 'create')

    def get_module_category(self, module_category_id):
        return self.client.invoke(ModuleService.service_id, 'getModuleCategory', module_category_id)

    def get_module_category_by_soft(self, soft_id):
        url = self.client.bbp_base_url + '/sys.auth.fu.dic?parentKey=' + soft_id
        return self.client.get_json(url)

    def exist_module_category_with_soft(self, soft_id, module_category_code):
        items = self.get_module_category_by_soft(soft_id)['items']
        ca_id = None

        find_category = list(
            filter(lambda i: isinstance(i['mcode'], str) and i['mcode'].startswith(module_category_code), items))
        if len(find_category) > 0:
            ca_id = find_category[0]['key']
        return len(find_category) > 0, ca_id

    def get_modules_by_ca(self, ca_id):
        url = self.client.bbp_base_url + '/sys.auth.fu.dic?parentKey=' + ca_id
        return self.client.get_json(url)

    def exist_module_with_ca_id(self, module_code, ca_id):
        items = self.get_modules_by_ca(ca_id)['items']
        module_id = None
        find_items = list(filter(lambda s: 'cd' in s['properties'] and s['properties']['cd'] == module_code, items))
        if len(find_items) > 0:
            module_id = find_items[0]['key']
        return len(find_items) > 0, module_id

    def save_module(self, soft_id, ca_id, module_code, module_name):
        req_obj = {"cd": module_code, "na": module_name, "des": "", "isShow": "1",
                   "softId": soft_id, "moduleCategoryId": ca_id, "moduleType": None,
                   "py": "", "wb": "", "zj": "", "ref": ""}
        return self.client.invoke(ModuleService.service_id, 'saveModule', req_obj, 'create')

    def get_module(self, module_id):
        return self.client.invoke(ModuleService.service_id, 'getModule', module_id)

    def save_module_prop(self, module_id, key, value, p_id=None, portal_type='WEB_DESKTOP_NATIVE', method='create'):

        req_obj = {"moduleId": module_id, "portalType": portal_type, "key": key, "value": value, "des": ""}
        if p_id is not None:
            req_obj["id"] = p_id
        return self.client.invoke(ModuleService.service_id, 'saveModuleProp', req_obj, method)

    # findModuleProp
    def find_module_prop(self, module_id, portal_type='WEB_DESKTOP_NATIVE'):
        return self.client.invoke(ModuleService.service_id, 'findModuleProp', module_id, portal_type)

    def exist_module_prop(self, key, module_id):
        properties = self.find_module_prop(module_id)
        found_item = [p for p in properties if p['key'] == key]
        if len(found_item) > 0:
            return found_item[0]
        else:
            return None


class ModuleNode(object):
    def __init__(self, data: dict):
        self.type = ''
        self.code = ''
        self.name = ''
        self.id = ''
        self.parent: 'ModuleNode' = None
        self.children = []
        for key, v in data.items():
            if key != CHILDREN:
                self.__dict__[key] = v

        if CHILDREN in data:
            for child in data[CHILDREN]:
                node = ModuleNode(child)
                node.parent = self
                self.children.append(node)

    def full_code(self):
        code = self.code
        parent = self.parent
        while parent is not None:
            code = parent.code + '-' + code
            parent = parent.parent
        return code

    def soft_id(self):
        if self.type == SOFT:
            return self.id
        elif self.parent is None:
            return None
        else:
            return self.parent.soft_id()

    def category_id(self):
        if self.type == CATEGORY:
            return self.id
        elif self.parent is None:
            return self.id
        else:
            return self.parent.category_id()


class ModuleConfig(object):

    def __init__(self, service: ModuleService):
        self.service = service

    def load(self, data):
        json_obj = json.loads(data)
        node = ModuleNode(json_obj)
        self.enter_node(node)

    def enter_node(self, node: ModuleNode):

        node_type = node.type

        if SOFT == node_type:
            self.enter_soft(node.code, node.name, node.children, node)
        elif CATEGORY == node_type:
            self.enter_category(node)
        else:
            self.enter_module(node)

    def enter_soft(self, soft_code, soft_name, children, node: ModuleNode):
        logging.info('-' * 30)
        logging.info('验证是否存产品:%s 代码:%s', soft_name, soft_code)
        b, soft_id = self.service.exist_soft(soft_code)

        if soft_id is None:
            logging.info('不存在产品:%s', soft_name)
            logging.info('添加产品:%s 代码:%s', soft_name, soft_code)
            soft = self.service.save_soft(soft_code, soft_name)
            soft_id = soft['id']
            logging.info('添加产品:%s成功, 产品id:%s', soft_name, soft_id)
        else:
            logging.info("找到产品:%s id:%s", soft_name, soft_id)

        node.id = soft_id

        for child in children:
            self.enter_node(child)

    def enter_category(self, node: ModuleNode):
        logging.info('-' * 30)
        logging.info('验证是否存分类:%s 代码:%s', node.name, node.full_code())
        soft_id = node.soft_id()
        b, ca_id = self.service.exist_module_category_with_soft(node.parent.id, node.full_code())
        if ca_id is None:
            logging.info('不存在分类:%s', node.name)
            logging.info('添加分类:%s 代码:%s', node.name, node.full_code())
            module_ca = self.service.save_module_category(node.full_code(), node.name, soft_id)
            ca_id = module_ca['id']
            logging.info('添加分类:%s成功, 分类id:%s', node.name, ca_id)

        else:
            logging.info("找到分类%s id:%s", node.name, ca_id)

        node.id = ca_id

        for child in node.children:
            self.enter_node(child)

    def enter_module(self, node):
        logging.info('-' * 30)
        logging.info('验证是存在功能:%s 代码:%s', node.name, node.full_code())
        soft_id = node.soft_id()
        ca_id = node.category_id()
        parent_id = ca_id if ca_id is not None else soft_id
        b, module_id = self.service.exist_module_with_ca_id(node.full_code(), parent_id)
        if module_id is None:
            logging.info('不存在功能:%s', node.name)
            logging.info('添加功能:%s 代码:%s', node.name, node.full_code())
            module = self.service.save_module(soft_id, ca_id, node.full_code(), node.name)
            module_id = module['id']
            logging.info('添加功能:%s成功, 功能id:%s', node.name, module_id)
        else:
            logging.info("找到功能%s id:%s", node.name, module_id)

        for k, v in node.__dict__.items():
            if k not in NODE_PROPERTIES:
                p = self.service.exist_module_prop(k, module_id)

                method = 'create' if p is None else 'update'
                p_id = p['id'] if p is not None and 'id' in p else None
                logging.info("为模块:%s 配置参数%s=%s method=%s", node.name, k, v, method)
                self.service.save_module_prop(module_id, k, v, p_id=p_id, method=method)


if __name__ == '__main__':
    import sys
    import os
    import codecs

    logging.basicConfig(stream=sys.stdout, level=logging.INFO)

    Default_tenant_id = 'dzgl'
    Default_username = 'system'
    Default_password = '123'
    Default_bbp_url = 'http://127.0.0.1:8080/bbp-server/'


    def tenant_client() -> SsdevClient:
        tenant = Default_tenant_id
        username = Default_username
        password = Default_password

        client = SsdevClient(Default_bbp_url, Default_bbp_url)
        rsp = client.my_roles(tenant, username, password)
        my_roles = rsp['body']
        token = client.fetch_token(tenant, username, my_roles[0])
        logging.info(token)

        return client


    root = os.path.sep.join(sys.argv[0].split(os.path.sep)[:-2])
    json_file = root + os.path.sep + 'balance-soft.json'
    data = ''

    with codecs.open(json_file, encoding='UTF-8') as f:
        data = f.read()
    service = ModuleService(tenant_client())
    mc = ModuleConfig(service)
    mc.load(data)
